#include "gui.h"
#include <cstdio>
#include <cstring>
#include <ctime>

const char* ORIGINAL_VIEW = "Original Image";
const char* CANVAS_VIEW = "Canvas(Mask)";
const char* OUTPUT_VIEW = "Output Image";
const int MAGNIFIER_MAX = 400;
const int MAGNIFIER_MIN = 10;
const int MAGNIFIER_SLIDER_SIZE = 25;
const int PENSIZE_MAX = 9;
const int PENSIZE_MIN = 0;
const int PENSIZE_SLIDER_SIZE = 3;
const Fl_Color PREDEFINED_COLORS[] = {FL_WHITE,FL_RED,FL_DARK_RED,FL_YELLOW,FL_DARK_YELLOW,FL_GREEN,FL_DARK_GREEN,FL_BLUE,FL_DARK_BLUE,FL_MAGENTA,FL_DARK_MAGENTA,FL_CYAN,FL_DARK_CYAN};

int getTime() {
	return clock()*100;
}

MainWindow *MainWindow::getMainWindow(Fl_Widget *o) {
	return ((MainWindow*)(o->parent()->user_data()));
}

char *MainWindow::getInputName() {return m_inputName;}
char *MainWindow::setInputName(const char *p_inputName) {strcpy(m_inputName,p_inputName); return m_inputName;}
char *MainWindow::getMaskName() {return m_maskName;}
char *MainWindow::setMaskName(const char *p_maskName) {strcpy(m_maskName,p_maskName); return m_maskName;}
char *MainWindow::getOutputName() {return m_outputName;}
char *MainWindow::setOutputName(const char *p_outputName){strcpy(m_outputName,p_outputName); return m_outputName;}

Color MainWindow::getColor() {return m_color;}
int MainWindow::setColor(double p_r, double p_g, double p_b) {m_color.setColor(p_r,p_g,p_b);return 1;}

int MainWindow::getRadius() {return m_radius;}
int MainWindow::getRadius2() {return m_radius*m_radius;}
int MainWindow::setRadius(int r) {return (m_radius=r);}

int MainWindow::setColorButton(Fl_Button *o) {m_button_current=o;return 1;}
Fl_Button *MainWindow::getColorButton() {return m_button_current;}
int MainWindow::setOnly(Fl_Button *o) {
	for(int i=0;i<26;i++)
		if(m_button_colors[i]!=o)
			m_button_colors[i]->clear();
	return 1;
}

int MainWindow::setMouseLeftDown() {m_mouse_leftDown=1; return 1;}
int MainWindow::setMouseRightDown() {m_mouse_rightDown=1; return 1;}
int MainWindow::setMouseLeftRelease() {m_mouse_leftDown=0; return 1;}
int MainWindow::setMouseRightRelease() {m_mouse_rightDown=0; return 1;}
int MainWindow::isMouseLeftDown() {return m_mouse_leftDown;}
int MainWindow::isMouseRightDown() {return m_mouse_rightDown; }

int MainWindow::getMouseX() {return m_mouse_posX;}
int MainWindow::getMouseY() {return m_mouse_posY;}
int MainWindow::setMouseX(int x){return (m_mouse_posX=x);}
int MainWindow::setMouseY(int y){return (m_mouse_posY=y);}
int MainWindow::setMouse(int x, int y){setMouseX(x);setMouseY(y);return 1;}
int MainWindow::isMouseMoved(int x, int y){return (abs(getMouseX()-x)+abs(getMouseY()-y));}

int MainWindow::getKeyFlag() {return m_key_flag;}
int MainWindow::setKeyFlag(int flag) {return (m_key_flag=flag);}
int MainWindow::isCtrlDown() {return m_key_flag&8;}
int MainWindow::isShiftDown() {return m_key_flag&16;}
int MainWindow::isAltDown() {return m_key_flag&32;}
int MainWindow::isKeyDown() {return m_key_flag;}
int MainWindow::isMouseDown() {return m_key_flag&7;}

int MainWindow::setLastMaskModified(int p) {return (m_lastMaskModified=p);}
int MainWindow::getLastMaskModified() {return m_lastMaskModified;}
int MainWindow::setLastMaskSaved(int p) {return (m_lastMaskSaved=p);}
int MainWindow::getLastMaskSaved() {return m_lastMaskSaved;}
int MainWindow::renewMaskTime() {
	clock_t t_time=getTime();
	setLastMaskModified(t_time);
	setLastMaskSaved(t_time);

	if(this->getActionList()!=NULL)
		getActionList()->updateRedoTime(t_time);
	return t_time;
}

int MainWindow::fastColorize() {
	int ret=0;
	if((Fl_Menu_Item*)(m_menuBar->find_item("&Colorization/Standard"))->value())
		ret=0;
	if((Fl_Menu_Item*)(m_menuBar->find_item("&Colorization/Fast"))->value())
		ret=1;
	if((Fl_Menu_Item*)(m_menuBar->find_item("&Colorization/Extreme Fast"))->value())
		ret=2;
	return ret;
}

int MainWindow::updateUndoRedo() {
	Fl_Menu_Item *t_item;
	t_item = (Fl_Menu_Item*)(getMenuBar()->find_item("O&ption/Redo..."));
	if(getActionList()->canRedo()) t_item->activate(); else t_item->deactivate();
	t_item = (Fl_Menu_Item*)(getMenuBar()->find_item("O&ption/Undo..."));
	if(getActionList()->canUndo()) t_item->activate(); else t_item->deactivate();
	return 1;
}

int MainWindow::setMagnifier(int p_factor,int type=IMAGE_INPUT|IMAGE_MASK|IMAGE_OUTPUT){if(type&IMAGE_INPUT)m_inputMagnifier=p_factor;if(type&IMAGE_MASK)m_maskMagnifier=p_factor;if(type&IMAGE_OUTPUT)m_outputMagnifier=p_factor;return 1;}
int MainWindow::getMagnifier(int type){int ret;if(type&IMAGE_INPUT)ret=m_inputMagnifier;if(type&IMAGE_MASK)ret=m_maskMagnifier;if(type&IMAGE_OUTPUT)ret=m_outputMagnifier;return ret;}

int cb_slider_magnifierHelper ( MainWindow *p, int type, int t );

int MainWindow::resize() {
	puts("Resize called");
	if(getInputImage()==NULL)
		return 0;
	int ratio=100, threshold=600;
	int t_max=max(getInputImage()->size().width*ratio/100.0,getInputImage()->size().height*ratio/100.0);

	if(t_max>threshold)
		ratio=(threshold*100.0/t_max);

	if(ratio<MAGNIFIER_MIN) ratio=MAGNIFIER_MIN;
	if(ratio>MAGNIFIER_MAX) ratio=MAGNIFIER_MAX;

	cb_slider_magnifierHelper(this,IMAGE_INPUT|IMAGE_MASK|IMAGE_OUTPUT,ratio);
	m_slider_inputMagnifier->value(ratio);
	m_slider_maskMagnifier->value(ratio);
	m_slider_outputMagnifier->value(ratio);

	return 1;
}

int MainWindow::setOutputImage(Mat &p) {
	if(m_outputImage==NULL)
		m_outputImage = new Mat();
	*m_outputImage=p.clone();
	return 1;
}

ActionBlock *MainWindow::setMatColor(Mat &m, int p_r, int p_c, int radius ) {

	int r,c,i,j,rs,re,cs,ce,t_blue,t_green,t_red;
	r=m.size().height; c=m.size().width;
	rs=max(0,p_r-radius); re=min(r-1,p_r+radius);
	cs=max(0,p_c-radius); ce=min(c-1,p_c+radius);
	m_color.getColor(t_red,t_green,t_blue);

	ActionBlock *ret = new ActionBlock((re-rs+1)*(ce-cs+1),"Fill in Color");
	unsigned char *p = &m.data[3*c*rs+3*cs];

	for(i=rs;i<=re;i++,p+=c+c+c)
		for(j=3*(ce-cs);j>=0;j-=3)
		{
			ret->addAction(Action(i,j/3+cs,0,Color(p[j+2],p[j+1],p[j+0])));
			p[j+0]=t_blue; p[j+1]=t_green; p[j+2]=t_red;
		}

	puts("Done setMatColor(Mat&,int,int,int)");
	return ret;

}

ActionBlock *MainWindow::setMatColor(Mat &m, Mat &p_input, int p_r, int p_c, int radius ) {
	
	int r,c,i,j,rs,re,cs,ce;
	r=m.size().height; c=m.size().width;
	rs=max(0,p_r-radius); re=min(r-1,p_r+radius);
	cs=max(0,p_c-radius); ce=min(c-1,p_c+radius);

	ActionBlock *ret = new ActionBlock((re-rs+1)*(ce-cs+1),"Fill in Color");
	unsigned char *p1 = &m.data[3*c*rs+3*cs];
	unsigned char *p2 = &p_input.data[3*c*rs+3*cs];

	for(i=rs;i<=re;i++,p1+=c+c+c,p2+=c+c+c)
		for(j=3*(ce-cs);j>=0;j-=3)
		{
			ret->addAction(Action(i,j/3+cs,0,Color(p1[j+2],p1[j+1],p1[j+0])));
			p1[j+0]=p2[j+0]; p1[j+1]=p2[j+1]; p1[j+2]=p2[j+2];
		}

	puts("DONE setMatColor(Mat&,Mat&,int,int,int)");
	return ret;

}

ActionBlock *MainWindow::getMatColor(Mat &m, int p_r, int p_c, int radius ) {
	
	int r,c,i,j,rs,re,cs,ce;
	r=m.size().height; c=m.size().width;
	rs=max(0,p_r-radius); re=min(r-1,p_r+radius);
	cs=max(0,p_c-radius); ce=min(c-1,p_c+radius);

	ActionBlock *ret = new ActionBlock((re-rs+1)*(ce-cs+1),"Fill in Color");
	unsigned char *p = &m.data[3*c*rs+3*cs];

	for(i=rs;i<=re;i++,p+=c+c+c)
		for(j=3*(ce-cs);j>=0;j-=3)
			ret->addAction(Action(i,j/3+cs,0,Color(p[j+2],p[j+1],p[j+0])));

	puts("DONE getMatColor(Mat&,int,int,int)");
	return ret;

}

Color MainWindow::getMatColor(Mat &m, int p_r, int p_c) {
	int r,c;
	r=m.size().height;
	c=m.size().width;
	unsigned char *p = &m.data[3*(c*p_r+p_c)];
	return Color((int)p[2],(int)p[1],(int)p[0]);
}

int MainWindow::doAction ( Mat &m, const ActionBlock *p_actionBlock ) {

	if(p_actionBlock==NULL)
		return 0;

	int r,c,i,j,k,len;
	r=m.size().height;
	c=m.size().width;
	len=p_actionBlock->getLen();

	int t_red,t_green,t_blue,t_radius,t_r,t_c;

	for(k=0;k<len;k++)
	{
		const Action *p_action=p_actionBlock->getAction(k);

		t_radius=p_action->getR();
		p_action->getColor(t_red,t_green,t_blue);
		t_r=p_action->getRow();
		t_c=p_action->getCol();

		int rs,re,cs,ce;
		rs=max(0,t_r-t_radius); re=min(r-1,t_r+t_radius);
		cs=max(0,t_c-t_radius); ce=min(c-1,t_c+t_radius);

		unsigned char *p = &m.data[3*c*rs+3*cs];
		for(i=rs;i<=re;i++)
		{
			for(j=3*(ce-cs);j>=0;j-=3)
			{
				p[j+0]=t_blue;
				p[j+1]=t_green;
				p[j+2]=t_red;
			}
			p+=c+c+c;
		}
	}

	if(p_actionBlock->getTime())
		setLastMaskModified(p_actionBlock->getTime());

	return 1;

}

Fl_File_Chooser *getFileChooser(const char* pathname, const char* pattern, int type, const char* title) {
	Fl_File_Chooser *chooser = new Fl_File_Chooser(pathname,pattern,type,title);
	chooser->show();
	while(chooser->shown()) Fl::wait();
	return chooser;
}

int MainWindow::showCanvas(int type=IMAGE_INPUT|IMAGE_MASK|IMAGE_OUTPUT) {
	if((getInputImage()==0)||(getMask()==0))
	{
		fl_alert("Please load an image first.");
		return 0;
	}

	if(type&IMAGE_INPUT)
	{
		namedWindow(ORIGINAL_VIEW,CV_WINDOW_FREERATIO);
		resizeWindow(ORIGINAL_VIEW,getInputImage()->size().width*getMagnifier(IMAGE_INPUT)/100.0,getInputImage()->size().height*getMagnifier(IMAGE_INPUT)/100.0);
		imshow(ORIGINAL_VIEW,*getInputImage());
	}

	if(type&IMAGE_MASK)
	{
		namedWindow(CANVAS_VIEW,CV_WINDOW_FREERATIO);
		resizeWindow(CANVAS_VIEW,getMask()->size().width*getMagnifier(IMAGE_MASK)/100.0,getMask()->size().height*getMagnifier(IMAGE_MASK)/100.0);
		imshow(CANVAS_VIEW,*getMask());
		setMouseCallback(CANVAS_VIEW,onCanvasMouseEvent,(this));
	}

	if(((type&IMAGE_OUTPUT)!=0)&&getOutputImage()!=NULL)
	{
		namedWindow(OUTPUT_VIEW,CV_WINDOW_FREERATIO);
		resizeWindow(OUTPUT_VIEW,getOutputImage()->size().width*getMagnifier(IMAGE_OUTPUT)/100.0,getMask()->size().height*getMagnifier(IMAGE_OUTPUT)/100.0);
		imshow(OUTPUT_VIEW,*getOutputImage());
	}
	return 1;
}

int MainWindow::initializeMask() {
	if(m_mask==NULL)
		m_mask = new Mat();
	*m_mask = m_inputImage->clone();
	renewMaskTime();
	updateUndoRedo();
	printf("In initializeMask()\n");
	printf("%d %d\n",m_mask,m_inputImage);
	return 1;
}

int MainWindow::clearImage(int type) {

	if(type&IMAGE_INPUT)
	{
		puts("Clearing input Image");
		if(m_inputImage!=NULL)
			delete m_inputImage;
		m_inputImage=NULL;
		destroyWindow(ORIGINAL_VIEW);
		puts("Input Image cleared");
	}
	if(type&IMAGE_MASK)
	{
		puts("Clearing mask");
		if(m_mask!=NULL)
			delete m_mask;
		m_mask=NULL;
		destroyWindow(CANVAS_VIEW);
		puts("Mask cleared");
		if(m_actionList)
			delete m_actionList;
		m_actionList = new ActionList();
		puts("HI");
		updateUndoRedo();
	}
	if(type&IMAGE_OUTPUT)
	{
		puts("Clearing output image");
		if(m_outputImage!=NULL)
			delete m_outputImage;
		m_outputImage=NULL;
		destroyWindow(OUTPUT_VIEW);
		puts("Output image cleared");
	}
	return 1;
}

bool checkExtension ( const char *str, int str_len, const char *pat, int pat_len ) {
	while(--str_len>=0&&--pat_len>=0&&toupper(str[str_len])==toupper(pat[pat_len]));
	return pat_len<0;
}

int MainWindow::saveImage(const Mat *img) {

	Fl_File_Chooser *chooser;
	if(img==getMask())
		chooser = getFileChooser(getMaskName(),"Image Files (*.{bmp,png,jpeg})",Fl_File_Chooser::CREATE,"Save the mask image");
	else if(img==getOutputImage())
		chooser = getFileChooser(getOutputName(),"Image Files (*.{bmp})",Fl_File_Chooser::CREATE,"Save the output image");
	else
	{
		fl_alert("Error occur: MainWindow::saveImage");
		return 1;
	}

	puts("Saving");
	if(chooser->value()!=NULL)
	{
		char st[MAX_FILENAME_SIZE]; strcpy(st,chooser->value()); int len=strlen(st);

		if ((img==getMask()&&!checkExtension(chooser->value(),len,".bmp",4))
			||(img==getOutputImage()&&!(checkExtension(chooser->value(),len,".bmp",4)&&checkExtension(chooser->value(),len,".png",4)&&checkExtension(chooser->value(),len,".jpeg",5))))
		{
			st[len]='.'; st[len+1]='b'; st[len+2]='m'; st[len+3]='p'; st[len+4]=0;
		}

		imwrite(st,*img);
		if(img==getMask())
			setMaskName(st);
		else setOutputName(st);
	}
	puts("Saved");
	return 1;
}

int MainWindow::loadImage(const char *path, int isInputImage=1 ) {
	Mat *img = new Mat();
	*img = imread(path);

	if(!(img->data))
	{
		fl_alert("The file is not a supported image");
		return 0;
	}

	if(isInputImage)
	{
		clearImage(IMAGE_INPUT|IMAGE_MASK|IMAGE_OUTPUT);
		m_inputImage=img;
	}
	else
	{
		if(img->size().width==m_inputImage->size().width&&img->size().height==m_inputImage->size().height)
		{
			clearImage(IMAGE_MASK);
			m_mask=img;
			renewMaskTime();
		}
		else
		{
			fl_alert("The loaded image size does not match the input image size");
			return 0;
		}
	}

	return 1;
}

int MainWindow::askToSaveImage(const char *msg, const Mat *img) {
	int ret=fl_choice(msg,"Cancel","No","Yes");

	if(ret==2)
	{
		saveImage(img);
		ret=1;
	}
	else if(ret==1)
	{
		ret=-1;
	}
	else if(ret==0)
	{
		ret=0;
	}

	return ret;
}

int MainWindow::askToSaveImages(int type) {

	if(getLastMaskModified()!=getLastMaskSaved()&&(type&IMAGE_MASK)&&getMask()!=NULL&&askToSaveImage("Do you want to save the image mask?",getMask())==0)
		return 0;
	if((type&IMAGE_OUTPUT)&&getOutputImage()&&askToSaveImage("Do you want to save the output image?",getOutputImage())==0)
		return 0;
	return 1;
}

void MainWindow::cb_button_loadImage(Fl_Widget *o, void *v) {
	
	MainWindow *p = getMainWindow(o);
	if(p->askToSaveImages(IMAGE_MASK|IMAGE_OUTPUT)==0)
		return;

	Fl_File_Chooser *chooser = getFileChooser(p->getInputName(),"Image Files (*.{bmp,gif,jpg,png})",Fl_File_Chooser::SINGLE,"Load an image");

	if(chooser->value()!=NULL&&p->loadImage(chooser->value()))
	{
		p->initializeMask();
		p->resize();
		p->showCanvas(IMAGE_INPUT|IMAGE_MASK);
		moveWindow(ORIGINAL_VIEW,40,200);
		moveWindow(CANVAS_VIEW,40+10+p->getInputImage()->size().width*p->getMagnifier(IMAGE_INPUT)/100.0,200);

		p->setInputName(chooser->value());
	}

	delete chooser;
}
void MainWindow::cb_button_saveImage(Fl_Widget *o, void *v) {

	MainWindow *p = getMainWindow(o);

	if(p->getOutputImage()==NULL)
	{
		fl_alert("No image has been colorized yet.");
		return;
	}

	p->saveImage(p->getOutputImage());

}
void MainWindow::cb_button_loadMask(Fl_Widget *o, void *v) {

	MainWindow *p = getMainWindow(o);

	if(p->getInputImage()==NULL)
	{
		fl_alert("Please load an image to initiate a mask");
		return;
	}

	if(p->askToSaveImages(IMAGE_MASK)==0)
		return;

	Fl_File_Chooser *chooser = getFileChooser(p->getMaskName(),"Image Files (*.{bmp,jpg,png})",Fl_File_Chooser::SINGLE,"Load a mask image");

	if(chooser->value()!=NULL&&p->loadImage(chooser->value(),0))
	{
		p->showCanvas(IMAGE_MASK);

		p->setMaskName(chooser->value());
	}

	delete chooser;
}
void MainWindow::cb_button_saveMask(Fl_Widget *o, void *v) {

	MainWindow *p = getMainWindow(o);
	if(p->getInputImage()==NULL)
	{
		fl_alert("Please load an image to initiate a mask");
		return;
	}
	p->saveImage(p->getMask());
	p->renewMaskTime();
	
}

void MainWindow::cb_colorChooser(Fl_Widget *o, void *v) {
	MainWindow *p = getMainWindow(o);
	Fl_Color_Chooser *q=(Fl_Color_Chooser*)o;
	unsigned char t_red=static_cast<unsigned char>(q->r()*255);
	unsigned char t_green=static_cast<unsigned char>(q->g()*255);
	unsigned char t_blue=static_cast<unsigned char>(q->b()*255);

	p->setColor(t_red,t_green,t_blue);
	if(p->getColorButton()&&((int)p->getColorButton()->user_data()>=13))
	{
		Fl_Color t_color=fl_rgb_color(t_red,t_green,t_blue);
		p->getColorButton()->color(t_color,t_color);
		p->getColorButton()->redraw();
	}
}

void MainWindow::cb_button_showViews(Fl_Widget *o, void *v) {
	getMainWindow(o)->showCanvas();
}

void MainWindow::cb_button_colorize(Fl_Widget *o, void *v) {

	MainWindow *p = getMainWindow(o);
	if(p->getInputImage()==NULL)
	{
		fl_alert("No image can be colorized");
		return;
	}
	if(fl_ask("Do you want to colorize the image now?"))
	{
		if(p->m_solver==NULL)
			p->m_solver = new Solver();

		if(p->m_outputImage!=NULL)
			p->clearImage(IMAGE_OUTPUT);

		p->setOutputImage(p->m_solver->solve(*p->getInputImage(),*p->getMask(),p->fastColorize()));
		p->showCanvas(IMAGE_OUTPUT);
		moveWindow(OUTPUT_VIEW,300,200);

		char st[100]; sprintf(st,"The image is colorized in %.3lf second(s).",p->m_solver->getSolvingTime());
		fl_alert(st);
	}
}

void MainWindow::cb_slider_radius(Fl_Widget *o, void *v ) {
	MainWindow *p = getMainWindow(o);
	int t=((Fl_Slider*)o)->value();
	p->setRadius(t);
	char s[10]; sprintf(s,"%d",t);
	p->m_input_radius->value(s);
}

void MainWindow::cb_input_radius(Fl_Widget *o, void *v) {
	MainWindow *p = getMainWindow(o);
	int t=atoi(((Fl_Input*)o)->value());
	if(t<PENSIZE_MIN) t=PENSIZE_MIN; if(t>PENSIZE_MAX) t=PENSIZE_MAX;
	p->setRadius(t);
	p->m_slider_radius->value(t);
	char s[10]; sprintf(s,"%d",t);
	p->m_input_radius->value(s);
}

int cb_button_doHelper(MainWindow *p, int isRedo) {
	if(p->getMask()==NULL)
		return 0;
	Mat &t_mask = *(p->getMask());
	const ActionBlock *p_actionBlock=isRedo?p->getActionList()->redo():p->getActionList()->undo();
	p->doAction(t_mask,p_actionBlock);
	p->updateUndoRedo();
	imshow(CANVAS_VIEW,t_mask);
	return 1;
}

void MainWindow::cb_button_undo(Fl_Widget *o, void *v) {
	cb_button_doHelper(getMainWindow(o),0);
}

void MainWindow::cb_button_redo(Fl_Widget *o, void *v) {
	cb_button_doHelper(getMainWindow(o),1);
}

void MainWindow::cb_button_clearMask(Fl_Widget *o,void *v) {
	MainWindow *p=getMainWindow(o);
	if(p->getMask()==NULL)
	{
		fl_alert("Please load an image to initiate a mask");
		return;
	}
	if(p->getLastMaskModified()!=p->getLastMaskSaved()&&p->askToSaveImage("Do you want to save the image mask?",p->getMask())==0)
		return;
	p->initializeMask();
	p->showCanvas(IMAGE_MASK);
}

int cb_slider_magnifierHelper ( MainWindow *p, int type, int t ) {
//	printf("In cb_slider_manigiferHelper (type,value) = (%d,%d)\n",type,t);
	p->setMagnifier(t,type);
	char s[10]; sprintf(s,"%d",t);
	if(type&IMAGE_INPUT)
		p->m_input_inputMagnifier->value(s);
	if(type&IMAGE_MASK)
		p->m_input_maskMagnifier->value(s);
	if(type&IMAGE_OUTPUT)
		p->m_input_outputMagnifier->value(s);
	if(p->getInputImage()!=NULL)
		p->showCanvas(type);
	return 0;
}

int cb_input_magnifierHelper ( Fl_Input *o, int type, int t ) {
//	printf("In cb_input_manigiferHelper (type,value) = (%d,%d)\n",type,t);
	MainWindow *p=MainWindow::getMainWindow(o);
	if(t>MAGNIFIER_MAX) t=MAGNIFIER_MAX; if(t<MAGNIFIER_MIN) t=MAGNIFIER_MIN;
	p->setMagnifier(t,type);
	if(type&IMAGE_INPUT)
		p->m_slider_inputMagnifier->value(t);
	if(type&IMAGE_MASK)
		p->m_slider_maskMagnifier->value(t);
	if(type&IMAGE_OUTPUT)
		p->m_slider_outputMagnifier->value(t);
	char s[3]; sprintf(s,"%d",t); o->value(s);
	if(p->getInputImage()!=NULL)
		p->showCanvas(type);
	return 0;
}

void MainWindow::cb_slider_inputMagnifier(Fl_Widget *o, void *v) {
	cb_slider_magnifierHelper(getMainWindow(o),IMAGE_INPUT,((Fl_Slider*)o)->value());
}

void MainWindow::cb_input_inputMagnifier(Fl_Widget *o, void *v) {
	cb_input_magnifierHelper((Fl_Input*)o,IMAGE_INPUT,atoi(((Fl_Input*)o)->value()));
}

void MainWindow::cb_slider_maskMagnifier(Fl_Widget *o, void *v) {
	cb_slider_magnifierHelper(getMainWindow(o),IMAGE_MASK,((Fl_Slider*)o)->value());
}

void MainWindow::cb_input_maskMagnifier(Fl_Widget *o, void *v) {
	cb_input_magnifierHelper((Fl_Input*)o,IMAGE_MASK,atoi(((Fl_Input*)o)->value()));
}

void MainWindow::cb_slider_outputMagnifier(Fl_Widget *o, void *v) {
	cb_slider_magnifierHelper(getMainWindow(o),IMAGE_OUTPUT,((Fl_Slider*)o)->value());
}

void MainWindow::cb_input_outputMagnifier(Fl_Widget *o, void *v) {
	cb_input_magnifierHelper((Fl_Input*)o,IMAGE_OUTPUT,atoi(((Fl_Input*)o)->value()));
}

void MainWindow::cb_button_colors(Fl_Widget *o, void *v) {
	Fl_Button *t_button=(Fl_Button*)o;
	MainWindow *p=getMainWindow(o);
	if(t_button->value())
	{
		p->setColorButton(t_button);
		Fl_Color t_color=o->color();
		unsigned char t_red,t_green,t_blue;
		Fl::get_color(t_color,t_red,t_green,t_blue);
		p->m_colorChooser->rgb(((int)t_red)/255.0,((int)t_green)/255.0,((int)t_blue)/255.0);
		p->setColor(((int)t_red),((int)t_green),((int)t_blue));
	}
	else
	{
		p->setColorButton(NULL);
	}
	p->setOnly(t_button);
}

void MainWindow::cb_button_exit(Fl_Widget *o, void *v) {
	getMainWindow(o)->askToSaveImages(IMAGE_MASK|IMAGE_OUTPUT);
	getMainWindow(o)->m_window_mainWindow->hide();
}

void MainWindow::onCanvasMouseEvent(int evt, int x, int y, int flag, void *param) {
	/*
	event:
		0 = normal
		1 = left down
		2 = right down
		3 = middle down
		4 = left release
		5 = right release
		6 = middle release
		7 = left double click
		8 = right double click
	flag:
		1 = left key
		2 = right key
		4 = middle key
		8 = ctrl key
		16 = shift key
		32 = alt key
	*/
	MainWindow *p = (MainWindow*)param;
	if(evt==1) p->setMouseLeftDown();
	if(evt==2) p->setMouseRightDown();
	if(evt==4) p->setMouseLeftRelease();
	if(evt==5) p->setMouseRightRelease();
	p->setKeyFlag(flag);
	
	int r=p->getInputImage()->size().height;
	int c=p->getInputImage()->size().width;

	printf("x y : %d %d\n",y,x);
//	printf("flag evt : %d %d\n",flag,evt);

	if(p->isCtrlDown()&&p->isMouseDown()&&p->getMask()!=NULL)
	{
		if(p->isMouseLeftDown())
			cb_button_doHelper(p,1);
		else if(p->isMouseRightDown())
			cb_button_doHelper(p,0);
	}
	else if(p->isMouseDown()&&(0<=y&&y<r&&0<=x&&x<c)&&p->isMouseMoved(x,y))
	{
		Mat &t_mask = *(p->getMask());
		ActionBlock *p_undoBlock, *p_redoBlock;
		ActionList *p_list=p->getActionList();

		if(p->isMouseLeftDown())
		{
			p_undoBlock=p->setMatColor(t_mask,y,x,p->getRadius());
			p_redoBlock=new ActionBlock(p->getRadius2(),"Fill in color");
			p_redoBlock->addAction(Action(y,x,p->getRadius(),p->getColor()));
		}
		else if(p->isMouseRightDown())
		{
			p_redoBlock=p->getMatColor(*p->getInputImage(),y,x,p->getRadius());
			p_undoBlock=p->setMatColor(t_mask,*p->getInputImage(),y,x,p->getRadius());
		}

		p_undoBlock->setTime(p->getLastMaskModified());
		p_redoBlock->setTime(p->setLastMaskModified(getTime()));

		p_list->push(p_redoBlock,p_undoBlock);

		imshow(CANVAS_VIEW,t_mask);

	}

	p->updateUndoRedo();
	p->setMouse(x,y);

}

void MainWindow::show() {
	m_window_mainWindow->show();
}

MainWindow::~MainWindow() {
	clearImage(IMAGE_INPUT|IMAGE_MASK|IMAGE_OUTPUT);
}

MainWindow::MainWindow() {

	m_solver=NULL;
	m_inputImage=m_mask=m_outputImage=NULL;
	m_color=Color(255,255,255);
	m_mouse_leftDown=m_mouse_rightDown=0;
	m_actionList=new ActionList();
	m_radius=3;
	m_pointerType=PTR_PEN;
	m_inputMagnifier=m_maskMagnifier=m_outputMagnifier=100;
	m_fastColorize=0;
	m_lastMaskModified=m_lastMaskSaved=0;
	m_mouse_posX=m_mouse_posY=-1;
	m_key_flag=0;
	m_button_current=0;
	strcpy(m_inputName,".");
	strcpy(m_maskName,".");
	strcpy(m_outputName,".");

	m_window_mainWindow = new Fl_Window(30,30,370,180,"Automatic Colorization");
	m_window_mainWindow->user_data((void*)(this));

		m_menuBar = new Fl_Menu_Bar(0,0,540,25);

			Fl_Menu_Item t_menuItems[] = {
				{"&File",0,0,0,FL_SUBMENU},
					{"Load Image...",FL_CTRL+'o',cb_button_loadImage},
					{"Save Image...",FL_CTRL+'s',cb_button_saveImage,0,FL_MENU_DIVIDER},
					{"Load Mask...",FL_SHIFT+'o',cb_button_loadMask},
					{"Save Mask...",FL_SHIFT+'s',cb_button_saveMask},
					{"Clear Mask",FL_SHIFT+'n',cb_button_clearMask,0,FL_MENU_DIVIDER},
					{"Exit",FL_CTRL+'q',cb_button_exit},
					{0},
				{"O&ption",'p',0,0,FL_SUBMENU},
					{"Show Views",FL_SHIFT+'t',cb_button_showViews},
					{"Undo...",FL_CTRL+'z',cb_button_undo},
					{"Redo...",FL_CTRL+'y',cb_button_redo},
					{0},
				{"&Colorization",0,0,0,FL_SUBMENU},
					{"Standard",0,0,0,FL_MENU_RADIO},
					{"Fast",0,0,0,FL_MENU_RADIO},
					{"Extreme Fast",0,0,0,FL_MENU_RADIO|FL_MENU_DIVIDER},
					{"Colorize",FL_CTRL+'c',cb_button_colorize},
					{0},
				{0},
			};

		m_menuBar->copy(t_menuItems);
		((Fl_Menu_Item*)m_menuBar->find_item("&Colorization/Standard"))->set();
		m_menuBar->user_data((void*)(this));

		m_colorChooser = new Fl_Color_Chooser(5,30,200,110);
		m_colorChooser->mode(0);
		m_colorChooser->rgb(1,1,1);
		m_colorChooser->user_data((void*)(this));
		m_colorChooser->callback(cb_colorChooser);

		m_input_radius = new Fl_Int_Input(210,40,30,20);
		m_input_radius->user_data((void*)(this));
		m_input_radius->value("3");
		m_input_radius->callback(cb_input_radius);
		
		m_slider_radius = new Fl_Slider(240,40,120,20,"Pen size");
		m_slider_radius->user_data((void*)(this));
		m_slider_radius->align(FL_ALIGN_TOP);
		m_slider_radius->type(FL_HOR_NICE_SLIDER);
		m_slider_radius->bounds(PENSIZE_MIN,PENSIZE_MAX);
		m_slider_radius->value(3);
		m_slider_radius->scrollvalue(3,PENSIZE_SLIDER_SIZE,PENSIZE_MIN,PENSIZE_MAX+PENSIZE_SLIDER_SIZE-PENSIZE_MIN);
		m_slider_radius->clear_visible_focus();
		m_slider_radius->callback(cb_slider_radius);
		
		m_input_inputMagnifier = new Fl_Int_Input(210,75,30,20);
		m_input_inputMagnifier->user_data((void*)(this));
		m_input_inputMagnifier->value("100");
		m_input_inputMagnifier->callback(cb_input_inputMagnifier);

		m_slider_inputMagnifier = new Fl_Slider(240,75,120,20,"Input Image size %");
		m_slider_inputMagnifier->user_data((void*)(this));
		m_slider_inputMagnifier->align(FL_ALIGN_TOP);;
		m_slider_inputMagnifier->type(FL_HOR_NICE_SLIDER);
		m_slider_inputMagnifier->bounds(MAGNIFIER_MIN,MAGNIFIER_MAX);
		m_slider_inputMagnifier->value(100);
		m_slider_inputMagnifier->scrollvalue(100,MAGNIFIER_SLIDER_SIZE,MAGNIFIER_MIN,MAGNIFIER_MAX+MAGNIFIER_SLIDER_SIZE-MAGNIFIER_MIN);
		m_slider_inputMagnifier->clear_visible_focus();
		m_slider_inputMagnifier->callback(cb_slider_inputMagnifier);
		
		m_input_maskMagnifier = new Fl_Int_Input(210,110,30,20);
		m_input_maskMagnifier->user_data((void*)(this));
		m_input_maskMagnifier->value("100");
		m_input_maskMagnifier->callback(cb_input_maskMagnifier);

		m_slider_maskMagnifier = new Fl_Slider(240,110,120,20,"Mask size %");
		m_slider_maskMagnifier->user_data((void*)(this));
		m_slider_maskMagnifier->align(FL_ALIGN_TOP);
		m_slider_maskMagnifier->type(FL_HOR_NICE_SLIDER);
		m_slider_maskMagnifier->bounds(MAGNIFIER_MIN,MAGNIFIER_MAX);
		m_slider_maskMagnifier->value(100);
		m_slider_maskMagnifier->scrollvalue(100,MAGNIFIER_SLIDER_SIZE,MAGNIFIER_MIN,MAGNIFIER_MAX+MAGNIFIER_SLIDER_SIZE-MAGNIFIER_MIN);
		m_slider_maskMagnifier->clear_visible_focus();
		m_slider_maskMagnifier->callback(cb_slider_maskMagnifier);

		m_input_outputMagnifier = new Fl_Int_Input(210,145,30,20);
		m_input_outputMagnifier->user_data((void*)(this));
		m_input_outputMagnifier->value("100");
		m_input_outputMagnifier->callback(cb_input_outputMagnifier);

		m_slider_outputMagnifier = new Fl_Slider(240,145,120,20,"Output size %");
		m_slider_outputMagnifier->user_data((void*)(this));
		m_slider_outputMagnifier->align(FL_ALIGN_TOP);
		m_slider_outputMagnifier->type(FL_HOR_NICE_SLIDER);
		m_slider_outputMagnifier->bounds(MAGNIFIER_MIN,MAGNIFIER_MAX);
		m_slider_outputMagnifier->value(100);
		m_slider_outputMagnifier->scrollvalue(100,MAGNIFIER_SLIDER_SIZE,MAGNIFIER_MIN,MAGNIFIER_MAX+MAGNIFIER_SLIDER_SIZE-MAGNIFIER_MIN);
		m_slider_outputMagnifier->clear_visible_focus();
		m_slider_outputMagnifier->callback(cb_slider_outputMagnifier);

		for(int i=0;i<26;i++)
		{
			m_button_colors[i] = new Fl_Button(7+(i%13)*15,142+(i/13)*15,15,15);
			m_button_colors[i]->user_data((void*)(i));
			m_button_colors[i]->type(FL_TOGGLE_BUTTON);
			if(i<13)
				m_button_colors[i]->color(PREDEFINED_COLORS[i],PREDEFINED_COLORS[i]);
			else m_button_colors[i]->color(FL_WHITE,FL_WHITE);
			m_button_colors[i]->clear_visible_focus();
			m_button_colors[i]->callback(cb_button_colors);
		}

	m_window_mainWindow->end();

}
